package com.meilai.project.job;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.meilai.project.constant.MessageCenterTypeEnum;
import com.meilai.project.entity.administration.goods.GoodsPutStorage;
import com.meilai.project.entity.business.contract.ContractApply;
import com.meilai.project.entity.business.customer.CustomerInfo;
import com.meilai.project.entity.business.sale.BillSale;
import com.meilai.project.entity.business.sale.BillSaleGroupRelation;
import com.meilai.project.entity.finance.paymentReceived.AccountReceived;
import com.meilai.project.entity.finance.paymentReceived.AccountReceivedBillSaleRelation;
import com.meilai.project.entity.reminder.ReminderConfig;
import com.meilai.project.entity.system.MessageCenter;
import com.meilai.project.mapper.business.contract.ContractApplyMapper;
import com.meilai.project.service.administration.goods.GoodsPutStorageService;
import com.meilai.project.service.business.contract.ContractApplyService;
import com.meilai.project.service.business.customer.CustomerInfoService;
import com.meilai.project.service.business.sale.BillSaleGroupRelationService;
import com.meilai.project.service.business.sale.BillSaleService;
import com.meilai.project.service.finance.paymentReceived.AccountReceivedBillSaleRelationService;
import com.meilai.project.service.finance.paymentReceived.AccountReceivedService;
import com.meilai.project.service.reminder.ReminderConfigService;
import com.meilai.project.service.system.MessageCenterService;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 合同提醒
 */
@Component
public class ContractReminderJob {

    @Autowired
    private ContractApplyMapper contractApplyMapper;

    @Autowired
    private ContractApplyService contractApplyService;
    @Autowired
    private CustomerInfoService customerInfoService;

    @Autowired
    private MessageCenterService messageCenterService;

    @Autowired
    private ReminderConfigService reminderConfigService;

    @Autowired
    private AccountReceivedService accountReceivedService;

    @Autowired
    private AccountReceivedBillSaleRelationService receivedBillSaleRelationService;

    @Autowired
    private BillSaleService billSaleService;

    @Autowired
    private BillSaleGroupRelationService billSaleGroupRelationService;

    @Autowired
    private GoodsPutStorageService goodsPutStorageService;

    /**
     * 每天早晨六点发送消息到消息中心提醒用户合同的相关提醒
     */
    @Scheduled(cron = "0 0 6 * * ?")
    @Transactional(rollbackFor = Exception.class)
    public void reminder() {
        BigDecimal zero = new BigDecimal("0");
        LocalDate now = LocalDate.now();

        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        // 获取所有的有效的合同
        List<ContractApply> list = contractApplyMapper.queryValidContract();
        // 查询公司所有的客户
        Map<Long, CustomerInfo> customerInfoMap = customerInfoService.companyListAll().stream().collect(Collectors.toMap(CustomerInfo::getId, Function.identity()));
        // 需要修改的合同信息
        Map<Long, ContractApply> map = new HashMap<>();
        // 需要保存的数据
        List<MessageCenter> messageCenterList = new ArrayList<>();
        // 读取合同提醒配置  id 1: 是催收提醒 2: 是催款提醒 3: 是到期提醒
        Map<Long, ReminderConfig> configMap = reminderConfigService.list().stream().collect(Collectors.toMap(ReminderConfig::getId, Function.identity()));
        // 催收提醒
        if (configMap.get(1L).getStatus()) {
            ReminderConfig reminderConfig = configMap.get(1L);
            // 过滤出需要提醒的合同
            List<ContractApply> data = list.stream().filter(contractApply -> (contractApply.getReceive_contract() == null || !contractApply.getReceive_contract()) && (contractApply.getContract_begin_date().plusDays(reminderConfig.getDay()).isBefore(now) || contractApply.getContract_begin_date().plusDays(reminderConfig.getDay()).equals(now))).collect(Collectors.toList());
            // 检查上次提醒时间是否符合当前频率
            data = data.stream().filter(contractApply -> contractApply.getLast_collection_reminder_date() == null || contractApply.getLast_collection_reminder_date().plusDays(reminderConfig.getFrequency()).equals(now)).collect(Collectors.toList());
            data.forEach(contractApply -> {
                CustomerInfo customerInfo = customerInfoMap.get(contractApply.getCustomer_id());
                if (customerInfo != null) {
                    long day = now.toEpochDay() - contractApply.getContract_begin_date().toEpochDay();
                    MessageCenter messageCenter = new MessageCenter();
                    messageCenter.setType(MessageCenterTypeEnum.TIP.getType());
                    messageCenter.setTitle("合同催收提醒");
                    messageCenter.setContent("您跟进的" + customerInfo.getName() + "客户合同编号为：" + contractApply.getContract_num() + "的合同距离开始执行时间已经过去" + day + "天没有给到财务结算材料，请尽快提交结算材料");
                    messageCenter.setUser_id(customerInfo.getMaintenance_staff_id());
                    messageCenter.setCreated_at(LocalDateTime.now());
                    messageCenterList.add(messageCenter);
                }
                contractApply.setLast_collection_reminder_date(now);
                // 加入map
                map.put(contractApply.getId(), contractApply);
            });
        }
        // 催款提醒
        if (configMap.get(2L).getStatus()) {
            ReminderConfig reminderConfig = configMap.get(2L);
            // 频率
            Integer frequency = reminderConfig.getFrequency();
            // 提前天数
            Integer day = reminderConfig.getDay();
            // 查出到款足额的销售单的id
            List<Long> ids = billSaleService.getFullPaymentBillSaleIds();
            if (CollectionUtils.isEmpty(ids)) {
                ids.add(-1L);
            }
            // 查出未足额的销售单
            List<BillSale> billSaleList = billSaleService.list(Wrappers.<BillSale>lambdaQuery().eq(BillSale::getFlow_status, 4).isNull(BillSale::getDeleted_at).eq(BillSale::getMode, 0).notIn(BillSale::getId, ids));
            billSaleList.forEach(item -> {
                if (item.getLatest_collection_time() != null) {
                    item.setCollection_time(item.getCollection_time());
                }
            });
            // 过滤掉未达到频率的和时间的销售单
            billSaleList = billSaleList.stream().filter(item -> (item.getCollection_time().plusDays(day).isAfter(now) || item.getCollection_time().plusDays(day).equals(now)) && (item.getLast_remind_date() == null || item.getLast_remind_date().plusDays(frequency).equals(now) || item.getLast_remind_date().plusDays(frequency).isBefore(now))).collect(Collectors.toList());
            // 查出销售单关联的组别信息
            List<Long> bill_sale_ids = billSaleList.stream().map(BillSale::getId).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(bill_sale_ids)) {
                bill_sale_ids.add(-1L);
            }
            List<Long> need_reminder_sale_ids = new ArrayList<>();
            List<BillSaleGroupRelation> relationList = new ArrayList<>();
            if(CollectionUtils.isNotEmpty(bill_sale_ids)) relationList = billSaleGroupRelationService.list(Wrappers.<BillSaleGroupRelation>lambdaQuery().in(BillSaleGroupRelation::getBill_sale_id, bill_sale_ids));
            Map<Long, List<Long>> group_relation_map = relationList.stream().collect(Collectors.groupingBy(BillSaleGroupRelation::getGroup_id, Collectors.mapping(BillSaleGroupRelation::getBill_sale_id, Collectors.toList())));
            List<Long> has_group_bill_sale_ids = new ArrayList<>();
            group_relation_map.forEach((key, value) -> {
                has_group_bill_sale_ids.addAll(value);
            });
            List<Long> group_ids = relationList.stream().map(BillSaleGroupRelation::getGroup_id).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(group_ids)) {
                group_ids.add(-1L);
            }
            // 查询出关联的到款信息和入库信息
            List<AccountReceived> receivedList = accountReceivedService.list(Wrappers.<AccountReceived>lambdaQuery().in(AccountReceived::getGroup_id, group_ids));
            List<GoodsPutStorage> putStorageList = goodsPutStorageService.list(Wrappers.<GoodsPutStorage>lambdaQuery().in(GoodsPutStorage::getGroup_id, group_ids));
            // 分组
            Map<Long, List<AccountReceived>> received_map = receivedList.stream().collect(Collectors.groupingBy(AccountReceived::getGroup_id));
            Map<Long, List<GoodsPutStorage>> put_storage_map = putStorageList.stream().collect(Collectors.groupingBy(GoodsPutStorage::getGroup_id));
            // 统计需要收到的金额和已经收到的钱
            for (Long groupId : group_ids) {
                BigDecimal decimal = BigDecimal.ZERO;
                BigDecimal money = BigDecimal.ZERO;
                List<Long> id_list = group_relation_map.get(groupId);
                List<BillSale> collect = billSaleList.stream().filter(item -> id_list.contains(item.getId())).collect(Collectors.toList());
                for (BillSale billSale : collect) {
                    if (billSale.getExecute_amount() != null) {
                        decimal = decimal.add(billSale.getExecute_amount());
                    }
                }
                List<AccountReceived> accountReceiveds = received_map.get(groupId);
                List<GoodsPutStorage> goodsPutStorages = put_storage_map.get(groupId);
                if (CollectionUtils.isNotEmpty(accountReceiveds)) {
                    for (AccountReceived accountReceived : accountReceiveds) {
                        money = money.add(accountReceived.getAccount());
                    }
                }
                if (CollectionUtils.isNotEmpty(goodsPutStorages)) {
                    for (GoodsPutStorage goodsPutStorage : goodsPutStorages) {
                        money = money.add(goodsPutStorage.getTotal_price());
                    }
                }
                if (money.compareTo(decimal) < 0) {
                    // 需要提醒的销售单
                    need_reminder_sale_ids.addAll(id_list);
                }
            }
            // 处理没有分组信息的销售单
            List<BillSale> collect = billSaleList.stream().filter(item -> !has_group_bill_sale_ids.contains(item.getId())).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(need_reminder_sale_ids)) {
                collect.addAll(billSaleList.stream().filter(item -> need_reminder_sale_ids.contains(item.getId())).collect(Collectors.toList()));
            }
            if (CollectionUtils.isNotEmpty(collect)) {
                // 查出关联的合同和客户信息
                List<Long> contract_ids = collect.stream().map(BillSale::getContract_id).collect(Collectors.toList());
                List<ContractApply> contractApplyList = contractApplyService.listByIds(contract_ids);
                List<Long> customer_ids = contractApplyList.stream().map(ContractApply::getCustomer_id).collect(Collectors.toList());
                Map<Long, CustomerInfo> customer_map = customerInfoService.listByIds(customer_ids).stream().collect(Collectors.toMap(CustomerInfo::getId, Function.identity()));
                Map<Long, ContractApply> contractApplyMap = contractApplyList.stream().collect(Collectors.toMap(ContractApply::getId, Function.identity()));
                for (BillSale billSale : collect) {
                    ContractApply contractApply = contractApplyMap.get(billSale.getContract_id());
                    CustomerInfo customerInfo = customer_map.get(contractApply.getCustomer_id());
                    MessageCenter messageCenter = new MessageCenter();
                    messageCenter.setType(MessageCenterTypeEnum.TIP.getType());
                    messageCenter.setTitle("销售单收款提醒");
                    messageCenter.setContent("您跟进的" + customerInfo.getName() + "客户合同编号为：" + contractApply.getContract_num() + "下的执行时间为" + dateTimeFormatter.format(billSale.getActual_execute_time_start()) + "~" + dateTimeFormatter.format(billSale.getActual_execute_time_end()) + "的销售单距离收款时间小于" + day + "天,请尽快催促客户打款");
                    messageCenter.setUser_id(customerInfo.getMaintenance_staff_id());
                    messageCenter.setCreated_at(LocalDateTime.now());
                    messageCenterList.add(messageCenter);
                }
                List<Long> list1 = collect.stream().map(BillSale::getId).collect(Collectors.toList());
                billSaleService.updateDate(now, list1);
            }
        }
        // 合同到期提醒
        if (configMap.get(3L).getStatus()) {
            ReminderConfig reminderConfig = configMap.get(3L);
            // 合同到款金额的map
            Map<Long, BigDecimal> contract_received_amount_map = new HashMap<>();
            // 过滤出快到期的合同
            List<ContractApply> data = list.stream().filter(contractApply -> (contractApply.getContract_end_date().minusDays(reminderConfig.getDay()).equals(now) || contractApply.getContract_end_date().minusDays(reminderConfig.getDay()).isBefore(now))).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(data)) {
                // 查询出到款信息
                List<AccountReceived> accountReceivedList = accountReceivedService.list(Wrappers.<AccountReceived>lambdaQuery().eq(AccountReceived::getDeleted, 0).eq(AccountReceived::getConfirm, 1));
                // 查询出到款信息和销售单的关系
                List<AccountReceivedBillSaleRelation> receivedBillSaleRelationList = receivedBillSaleRelationService.list(Wrappers.<AccountReceivedBillSaleRelation>lambdaQuery().in(AccountReceivedBillSaleRelation::getAccount_received_id, accountReceivedList.stream().map(AccountReceived::getId).collect(Collectors.toList())));
                // 查询出销售单信息 并整理出合同和销售单的对应关系
                Map<Long, List<Long>> contract_sale_map = billSaleService.list(Wrappers.<BillSale>lambdaQuery().in(BillSale::getContract_id, data.stream().map(ContractApply::getId).collect(Collectors.toList())).isNull(BillSale::getDeleted_at).eq(BillSale::getMode, 0)).stream().collect(Collectors.groupingBy(BillSale::getContract_id, Collectors.mapping(BillSale::getId, Collectors.toList())));
                data.forEach(contractApply -> {
                    List<Long> bill_sale_ids = contract_sale_map.get(contractApply.getId());
                    if (CollectionUtils.isNotEmpty(bill_sale_ids)) {
                        List<Long> received_ids = receivedBillSaleRelationList.stream().filter(item -> bill_sale_ids.contains(item.getBill_sale_id())).map(AccountReceivedBillSaleRelation::getAccount_received_id).collect(Collectors.toList());
                        List<AccountReceived> collect = accountReceivedList.stream().filter(item -> received_ids.contains(item.getId())).collect(Collectors.toList());
                        BigDecimal sum = new BigDecimal("0");
                        for (AccountReceived accountReceived : collect) {
                            sum = sum.add(accountReceived.getAccount());
                        }
                        contract_received_amount_map.put(contractApply.getId(), sum);
                    }
                });
            }
            // 过滤出执行金额满足条件的合同信息
            data = data.stream().filter(contractApply -> {
                BigDecimal sum = contract_received_amount_map.get(contractApply.getId());
                BigDecimal divide = reminderConfig.getExecution_percentage().divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP);
                return sum == null || sum.compareTo(zero) == 0 || contractApply.getDiscounted_price().divide(sum, 4, RoundingMode.HALF_UP).compareTo(divide) < 0;
            }).collect(Collectors.toList());
            // 检查上次提醒时间是否符合当前频率
            data = data.stream().filter(contractApply -> contractApply.getLast_expire_reminder_date() == null || contractApply.getLast_expire_reminder_date().plusDays(reminderConfig.getFrequency()).equals(now)).collect(Collectors.toList());
            DecimalFormat format = new DecimalFormat("0.00");
            data.forEach(contractApply -> {
                CustomerInfo customerInfo = customerInfoMap.get(contractApply.getCustomer_id());
                if (customerInfo != null) {
                    long day = contractApply.getContract_end_date().toEpochDay() - now.toEpochDay();
                    MessageCenter messageCenter = new MessageCenter();
                    messageCenter.setType(MessageCenterTypeEnum.TIP.getType());
                    messageCenter.setTitle("合同到期提醒");
                    messageCenter.setContent("您跟进的" + customerInfo.getName() + "客户合同编号为：" + contractApply.getContract_num() + "的合同当前执行金额为小于" + format.format(reminderConfig.getExecution_percentage()) + "%" + "且距离执行结束时间还有" + day + "天,请及时添加补充协议");
                    messageCenter.setUser_id(customerInfo.getMaintenance_staff_id());
                    messageCenter.setCreated_at(LocalDateTime.now());
                    messageCenterList.add(messageCenter);
                }
                ContractApply contract = map.get(contractApply.getId());
                if (contract != null) {
                    contract.setLast_expire_reminder_date(now);
                } else {
                    contractApply.setLast_expire_reminder_date(now);
                    // 加入map
                    map.put(contractApply.getId(), contractApply);
                }
            });
        }
        if (CollectionUtils.isNotEmpty(messageCenterList)) {
            messageCenterService.saveBatch(messageCenterList);
        }
        if (CollectionUtils.isNotEmpty(map.values())) {
            for (ContractApply value : map.values()) {
                contractApplyService.update(Wrappers.<ContractApply>lambdaUpdate()
                        .set(ContractApply::getLast_collection_reminder_date, value.getLast_collection_reminder_date())
                        .set(ContractApply::getLast_press_for_money_reminder_date, value.getLast_press_for_money_reminder_date())
                        .set(ContractApply::getLast_expire_reminder_date, value.getLast_expire_reminder_date())
                        .eq(ContractApply::getId, value.getId()));
            }
        }
    }
}
